<!DOCTYPE html>
<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="tong.li&#39;s blog">
  <meta name="keyword" content="彤哥哥博客，95后技术爱好者,现就职于同程旅行/同程艺龙上海分公司，专注于互联网技术分享的平台。">
  
    <link rel="shortcut icon" href="/css/images/icon.png">
  
  <title>
    
      Zookeeper专题-Zookeeper入门 | 彤哥哥的博客
    
  </title>
  <link href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="https://cdn.staticfile.org/highlight.js/9.12.0/styles/tomorrow-night.min.css" rel="stylesheet">
  
<link rel="stylesheet" href="/css/style.css">

  
  <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://cdn.staticfile.org/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="https://cdn.staticfile.org/nprogress/0.2.0/nprogress.min.js"></script>
  
    
<script src="/js/qrious.js"></script>

  
  
  
  
    <!-- MathJax support START -->
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="https://cdn.staticfile.org/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <!-- MathJax support END -->
  


  
  
    
<script src="/js/local-search.js"></script>


<meta name="generator" content="Hexo 5.4.2"></head>
<div class="wechat-share">
  <img src="/css/images/logo.png" />
</div>
  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>彤哥哥的博客</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">主页</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/series/" class="item-link">分类</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">标签</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">归档</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/project/" class="item-link">项目</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">关于</a>
          
        </li>
      
      
        <li class="menu-item menu-item-search right-list">
    <a role="button" class="popup-trigger">
        <i class="fa fa-search fa-fw"></i>
    </a>
</li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">主页</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/series/" class="menu-link">分类</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">标签</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">归档</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/project/" class="menu-link">项目</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">关于</a>
            
          </li>
        
      </ul>
    </div>
    
      <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
            <span class="search-icon">
                <i class="fa fa-search"></i>
            </span>
            <div class="search-input-container">
                <input autocomplete="off" autocapitalize="off"
                    placeholder="Please enter your keyword(s) to search." spellcheck="false"
                    type="search" class="search-input">
            </div>
            <span class="popup-btn-close">
                <i class="fa fa-times-circle"></i>
            </span>
        </div>
        <div id="search-result">
            <div id="no-result">
                <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
            </div>
        </div>
    </div>
</div>
    
  </div>
</header>

    <div id="article-banner">
  <h2>Zookeeper专题-Zookeeper入门</h2>
  <p class="post-date">2022-05-07</p>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><h1 id="1-前言"><a href="#1-前言" class="headerlink" title="1.前言"></a>1.前言</h1><p>我们打开<a target="_blank" rel="noopener" href="https://zookeeper.apache.org/">Zookeeper</a>官网，先大致了解一下Zookeeper的官方描述：</p>
<blockquote>
<p>Apache ZooKeeper 致力于开发和维护一个开源服务器，以实现高度可靠的分布式协调。</p>
</blockquote>
<p>Zookeeper其实是<a target="_blank" rel="noopener" href="https://hadoop.apache.org/">Apache Hadoop</a>的子项目，学习大数据以及分布式领域的必备技能。</p>
<h2 id="1-1-应用场景"><a href="#1-1-应用场景" class="headerlink" title="1.1 应用场景"></a>1.1 应用场景</h2><ul>
<li><p>统一命名服务：服务注册与发现</p>
</li>
<li><p>统一配置管理：配置中心</p>
</li>
<li><p>统一集群管理:  集群选举</p>
</li>
<li><p>分布式同步：应用监控(服务动态节点上下线)</p>
</li>
<li><p>软负载均衡：服务负载</p>
</li>
<li><p>分布式锁：资源互斥</p>
</li>
</ul>
<h2 id="1-2-工作机制"><a href="#1-2-工作机制" class="headerlink" title="1.2 工作机制"></a>1.2 工作机制</h2><p>Zookeeper本质上是一个基于分布式的小文件存储系统，它基于类似于文件系统目录树的方式进行数据结构存储，并且可以对树中的节点进行有效的管理和监听。</p>
<p>从设计模式角度来理解，Zookeeper就是一个基于<strong>观察者-模式</strong>设计的分布式服务框架。</p>
<p>Zookeeper负责节点数据的存储和管理。每个Follower服务节点都会同步Leader节点的数据。</p>
<p>另外，还接受观察者的注册，一旦节点或节点中的数据发生变化，Zookeeper将负责通知已在Zookeeper上注册的观察者，最终观察者做出处理。</p>
<h2 id="1-3-集群角色"><a href="#1-3-集群角色" class="headerlink" title="1.3 集群角色"></a>1.3 集群角色</h2><p><img src="https://ltyeamin.github.io/imgs/2022/05/20220516205247.png"></p>
<ul>
<li><p>Leader：集群工作的核心，事务请求（写操作）的唯一调度和处理者，保证集群事务处理的顺序性；集群内部各个服务器的调度者。</p>
</li>
<li><p>Follower: 处理客户端非事务（读操作） 请求，转发事务请求给 Leader;参与集群 Leader选举投票。</p>
</li>
<li><p>Observer: 观察者角色，观察Zookeeper集群的最新状态变化并将这些状态同步过来，其对于非事务请求可以进行独立处理，对于事务请求，则会转发给 Leader服务器进行处理。不会参与任何形式的投票只提供非事务服务，通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力</p>
</li>
</ul>
<h2 id="1-4-功能特性"><a href="#1-4-功能特性" class="headerlink" title="1.4 功能特性"></a>1.4 功能特性</h2><ul>
<li><p>高可用性(HA)：分布式集群模式下，一个Leader，多个Follower组成的集群，集群节点为2N+1，只要存活半数以上的节点，那么集群就可以正常服务。如果消息被其中一台服务器接受，那么将被所有的服务器接受。</p>
</li>
<li><p>全局数据一致性：每个Server节点保存相同的数据副本，Client无论连接到哪个Server节点，那么展示的数据都是一致的。</p>
</li>
<li><p>有序性：分为全局有序和偏序两种。假设，在一台Server上消息a在消息b前发布，所有Server上消息a都将在消息b前被发布，那么称之为全局有序。若一个消息b在消息a后被同一个发送者发布， a必将排在b前面，则称之为偏序。</p>
</li>
<li><p>数据更新原子性：支持事务模型，一次数据更新要么成功（半数以上节点成功），要么失败，不存在中间状态。对于 create， setData， delete 等有写操作的请求，则需要统一转发给leader 处理， leader需要决定编号、执行操作，这个过程称为一个事务。</p>
</li>
<li><p>实时性：Zookeeper保证客户端将在一个时间间隔范围内能够读取到最新数据。</p>
</li>
<li><p>高性能：ZooKeeper 应用程序在数千台机器上运行，它在读取比写入更常见的情况下表现最佳，请参阅<a target="_blank" rel="noopener" href="https://zookeeper.apache.org/doc/r3.8.0/zookeeperOver.html#zkPerfRW">ZooKeeper 吞吐量随着读写比率的变化</a>。</p>
</li>
</ul>
<h2 id="1-5-数据模型"><a href="#1-5-数据模型" class="headerlink" title="1.5 数据模型"></a>1.5 数据模型</h2><p>Zookeeper的数据结构与Unix文件系统很类似，整体可以看做是个树。</p>
<p>树中的每个节点称为ZNode，每个Znode能够存储1MB的数据。</p>
<p>节点名称是由斜杠 (/) 分隔的一系列路径元素，每个ZNode都有唯一的路径标识。</p>
<p><img src="https://ltyeamin.github.io/imgs/2022/05/20220516211939.png"></p>
<p>每个Znode包含以下三部分：</p>
<ul>
<li><p>stat：节点状态信息，描述节点的版本、权限等信息。</p>
</li>
<li><p>data：当前节点所关联的数据。</p>
</li>
<li><p>children：当前节点下的子节点信息。</p>
</li>
</ul>
<h1 id="2-Zookeeper使用"><a href="#2-Zookeeper使用" class="headerlink" title="2. Zookeeper使用"></a>2. Zookeeper使用</h1><h2 id="2-1-Zookeeper搭建"><a href="#2-1-Zookeeper搭建" class="headerlink" title="2.1 Zookeeper搭建"></a>2.1 Zookeeper搭建</h2><p>我这里搭建只是Windows单机本地模式，集群搭建模式请移步<a href="/2019/11/27/Zookeeper%E9%9B%86%E7%BE%A4%E6%90%AD%E5%BB%BA/">Zookeeper集群搭建</a>。</p>
<p>先去官网[下载页面](<a target="_blank" rel="noopener" href="https://zookeeper.apache.org/releases.html">Apache ZooKeeper</a>)下载一个合适的Zookeeper安装包，我这里选择的是3.7.1版本。</p>
<p>下载解压后<a target="_blank" rel="noopener" href="https://dlcdn.apache.org/zookeeper/zookeeper-3.7.1/apache-zookeeper-3.7.1-bin.tar.gz"><strong>apache-zookeeper-3.7.1-bin.tar.gz</strong></a>出现apache-zookeeper-3.7.1-bin目录。</p>
<p>进入到apache-zookeeper-3.7.1-bin/目录里，我们创建一个data文件夹，用于存放Zookeeper运行时的数据。</p>
<p><img src="https://ltyeamin.github.io/imgs/2022/05/20220517221651.png"></p>
<p>进入到apache-zookeeper-3.7.1-bin/conf目录里，复制zoo_sample.cfg一份出来，重命名为zoo.cfg，并将配置文件里的dataDir目录改为刚才创建<strong>data的绝对路径</strong>。</p>
<p><img src="D:\MyConfiguration\TCLDUSER\AppData\Roaming\marktext\images\2022-05-17-22-17-55-image.png"> </p>
<h2 id="2-2-Zookeeper使用"><a href="#2-2-Zookeeper使用" class="headerlink" title="2.2 Zookeeper使用"></a>2.2 Zookeeper使用</h2><h3 id="2-2-1-基本命令行"><a href="#2-2-1-基本命令行" class="headerlink" title="2.2.1 基本命令行"></a>2.2.1 基本命令行</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ ./zkServer.sh  start  <span class="comment">#启动服务</span></span><br><span class="line">ZooKeeper JMX enabled by default</span><br><span class="line">Using config: D:\program\sdk\apache-zookeeper-3.7.1-bin\conf\zoo.cfg</span><br><span class="line">Starting zookeeper ... STARTED</span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ ./zkServer.sh  status  <span class="comment"># 查看服务状态</span></span><br><span class="line">ZooKeeper JMX enabled by default</span><br><span class="line">Using config: D:\program\sdk\apache-zookeeper-3.7.1-bin\conf\zoo.cfg</span><br><span class="line">Client port found: 2181. Client address: localhost. Client SSL: <span class="literal">false</span>.</span><br><span class="line">Mode: standalone</span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ ./zkServer.sh  restart  <span class="comment"># 重启服务</span></span><br><span class="line">ZooKeeper JMX enabled by default</span><br><span class="line">Using config: D:\program\sdk\apache-zookeeper-3.7.1-bin\conf\zoo.cfg</span><br><span class="line">ZooKeeper JMX enabled by default</span><br><span class="line">Using config: D:\program\sdk\apache-zookeeper-3.7.1-bin\conf\zoo.cfg</span><br><span class="line">Stopping zookeeper ... STOPPED</span><br><span class="line">ZooKeeper JMX enabled by default</span><br><span class="line">Using config: D:\program\sdk\apache-zookeeper-3.7.1-bin\conf\zoo.cfg</span><br><span class="line">Starting zookeeper ... STARTED</span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ ./zkServer.sh  stop <span class="comment"># 关闭服务</span></span><br><span class="line">ZooKeeper JMX enabled by default</span><br><span class="line">Using config: D:\program\sdk\apache-zookeeper-3.7.1-bin\conf\zoo.cfg</span><br><span class="line">Stopping zookeeper ... STOPPED</span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ./zkCli.sh <span class="comment"># 客戶端连接到本地服务</span></span><br><span class="line">$ ./zkCli.sh -server 192.168.1.101:2181 <span class="comment"># 客戶端连接到远程服务</span></span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 1] version <span class="comment"># 查看客户端版本</span></span><br><span class="line">ZooKeeper CLI version: 3.7.1-a2fb57c55f8e59cdd76c34b357ad5181df1258d5, built on 2022-05-07 06:45 UTC</span><br><span class="line"></span><br><span class="line">[zk: localhost:2181(CONNECTED) 2] <span class="built_in">help</span>   <span class="comment"># 查看帮助命令</span></span><br><span class="line">ZooKeeper -server host:port -client-configuration properties-file cmd args</span><br><span class="line">        addWatch [-m mode] path <span class="comment"># optional mode is one of [PERSISTENT, PERSISTENT_RECURSIVE] - default is PERSISTENT_RECURSIVE</span></span><br><span class="line">        addauth scheme auth</span><br><span class="line">        close</span><br><span class="line">        config [-c] [-w] [-s]</span><br><span class="line">        connect host:port</span><br><span class="line">        create [-s] [-e] [-c] [-t ttl] path [data] [acl]</span><br><span class="line">        delete [-v version] path</span><br><span class="line">        deleteall path [-b batch size]</span><br><span class="line">        delquota [-n|-b|-N|-B] path</span><br><span class="line">        get [-s] [-w] path</span><br><span class="line">        getAcl [-s] path</span><br><span class="line">        getAllChildrenNumber path</span><br><span class="line">        getEphemerals path</span><br><span class="line">        <span class="built_in">history</span></span><br><span class="line">        listquota path</span><br><span class="line">        <span class="built_in">ls</span> [-s] [-w] [-R] path</span><br><span class="line">        printwatches on|off</span><br><span class="line">        quit</span><br><span class="line">        reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*]</span><br><span class="line">        redo cmdno</span><br><span class="line">        removewatches path [-c|-d|-a] [-l]</span><br><span class="line">        <span class="built_in">set</span> [-s] [-v version] path data</span><br><span class="line">        setAcl [-s] [-v version] [-R] path acl</span><br><span class="line">        setquota -n|-b|-N|-B val path</span><br><span class="line">        <span class="built_in">stat</span> [-w] path</span><br><span class="line">        <span class="built_in">sync</span> path</span><br><span class="line">        version</span><br><span class="line">        <span class="built_in">whoami</span></span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 3] <span class="built_in">ls</span> /  <span class="comment"># 查看节点目录</span></span><br><span class="line">[zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 4] <span class="built_in">ls</span> / -s  <span class="comment"># 查看节点详细信息</span></span><br><span class="line">[zookeeper]</span><br><span class="line">cZxid = 0x0    <span class="comment"># znode创建节点的事务zxid</span></span><br><span class="line">ctime = Thu Jan 01 08:00:00 CST 1970  <span class="comment"># znode创建时间</span></span><br><span class="line">mZxid = 0x0    <span class="comment"># znode最后更新的事务zxid </span></span><br><span class="line">mtime = Thu Jan 01 08:00:00 CST 1970   <span class="comment"># znode最后一次更新时间</span></span><br><span class="line">pZxid = 0x0    <span class="comment"># znode最后更新的子节点zxid</span></span><br><span class="line">cversion = -1  <span class="comment"># znode子节点版本号，znode子节点修改次数</span></span><br><span class="line">dataVersion = 0 <span class="comment"># znode数据版本号</span></span><br><span class="line">aclVersion = 0  <span class="comment"># znode访问控制列表的版本号</span></span><br><span class="line">ephemeralOwner = 0x0  <span class="comment"># 若znode是临时节点，这个是znode拥有者的sessionId。若不是临时节点为0</span></span><br><span class="line">dataLength = 0  <span class="comment"># znode的数据长度</span></span><br><span class="line">numChildren = 1 <span class="comment"># znode子节点数量</span></span><br></pre></td></tr></table></figure>

<h3 id="2-2-2-节点类型"><a href="#2-2-2-节点类型" class="headerlink" title="2.2.2 节点类型"></a>2.2.2 节点类型</h3><p>持久节点：Persistent，客户端与服务器端断开连接后，创建的节点不删除。</p>
<p>短暂节点：Ephemeral，客户端与服务器端断开连接后，创建的节点自动删除。</p>
<p>顺序节点：创建同名节点时，节点后会自动递增计号，全局有序。</p>
<p>非顺序节点： 创建同名节点时，会创建失败。</p>
<h3 id="2-2-3-节点操作"><a href="#2-2-3-节点操作" class="headerlink" title="2.2.3 节点操作"></a>2.2.3 节点操作</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 16] create /mingzhu <span class="string">&quot;四大名著&quot;</span> <span class="comment"># 创建永久节点并设置数据</span></span><br><span class="line"></span><br><span class="line">WATCHER::</span><br><span class="line">Created /mingzhu</span><br><span class="line">WatchedEvent state:SyncConnected <span class="built_in">type</span>:NodeChildrenChanged path:/</span><br><span class="line">[zk: localhost:2181(CONNECTED) 18] create /mingzhu/xiyouji <span class="string">&quot;孙悟空&quot;</span> <span class="comment"># 多级目录创建</span></span><br><span class="line">Created /mingzhu/xiyouji</span><br><span class="line">[zk: localhost:2181(CONNECTED) 19] <span class="built_in">ls</span> /  <span class="comment"># 查看创建的mingzhu</span></span><br><span class="line">[mingzhu, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 20] <span class="built_in">ls</span> /mingzhu  <span class="comment"># 查看创建的xiyouji</span></span><br><span class="line">[xiyouji]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 21] get /mingzhu/xiyouji <span class="comment"># 获取已创建节点的数据</span></span><br><span class="line">孙悟空</span><br><span class="line">[zk: localhost:2181(CONNECTED) 24] <span class="built_in">set</span> /mingzhu/xiyouji <span class="string">&quot;玉皇大帝&quot;</span> <span class="comment"># 修改数据</span></span><br><span class="line">[zk: localhost:2181(CONNECTED) 25] get /mingzhu/xiyouji</span><br><span class="line">玉皇大帝</span><br><span class="line">[zk: localhost:2181(CONNECTED) 26] get -s /mingzhu/xiyouji <span class="comment"># 获取节点详情</span></span><br><span class="line">玉皇大帝</span><br><span class="line">cZxid = 0xf</span><br><span class="line">ctime = Tue May 17 23:56:17 CST 2022</span><br><span class="line">mZxid = 0x10       <span class="comment"># znode最后更新的事务zxid也变了</span></span><br><span class="line">mtime = Wed May 18 00:00:58 CST 2022  <span class="comment"># 修改的时间</span></span><br><span class="line">pZxid = 0xf</span><br><span class="line">cversion = 0</span><br><span class="line">dataVersion = 1  <span class="comment"># 数据版本增加了</span></span><br><span class="line">aclVersion = 0</span><br><span class="line">ephemeralOwner = 0x0</span><br><span class="line">dataLength = 12  <span class="comment"># 玉皇大帝占用了四个汉字12个字节</span></span><br><span class="line">numChildren = 0</span><br><span class="line">[zk: localhost:2181(CONNECTED) 31] create /test  <span class="comment"># 重新创建test节点</span></span><br><span class="line">Created /test</span><br><span class="line">[zk: localhost:2181(CONNECTED) 32] <span class="built_in">ls</span> /</span><br><span class="line">[mingzhu, <span class="built_in">test</span>, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 33] delete /test  <span class="comment"># 删除test节点   </span></span><br><span class="line">[zk: localhost:2181(CONNECTED) 34] <span class="built_in">ls</span> /</span><br><span class="line">[mingzhu, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 35] deleteall /mingzhu <span class="comment"># 递归删除所有节点，慎用啊</span></span><br><span class="line">[zk: localhost:2181(CONNECTED) 36] <span class="built_in">ls</span> /</span><br><span class="line">[zookeeper]</span><br></pre></td></tr></table></figure>

<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 43] create  /litong <span class="string">&quot;彤哥哥&quot;</span> <span class="comment"># 创建永久节点</span></span><br><span class="line">Created /litong</span><br><span class="line">[zk: localhost:2181(CONNECTED) 44] <span class="built_in">ls</span> /</span><br><span class="line">[litong, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 47] create -s /litong <span class="string">&quot;彤哥哥&quot;</span>  <span class="comment"># 创建顺序节点</span></span><br><span class="line">Created /litong0000000004</span><br><span class="line">[zk: localhost:2181(CONNECTED) 48] <span class="built_in">ls</span> /</span><br><span class="line">[litong, litong0000000003, litong0000000004, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 49] quit  <span class="comment"># 退出客户端</span></span><br><span class="line">[zk: localhost:2181(CONNECTED) 0] <span class="built_in">ls</span> /   <span class="comment"># 退出后重连，永久节点仍然存在</span></span><br><span class="line">[litong, litong0000000003, litong0000000004, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 1] create -e /zhangsan <span class="string">&quot;张三&quot;</span></span><br><span class="line">Created /zhangsan</span><br><span class="line">[zk: localhost:2181(CONNECTED) 2] <span class="built_in">ls</span> /</span><br><span class="line">[litong, litong0000000003, litong0000000004, zhangsan, zookeeper]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 4] quit  <span class="comment"># 退出重进</span></span><br><span class="line">[zk: localhost:2181(CONNECTED) 1] <span class="built_in">ls</span> /  <span class="comment"># 客户端一旦断开，临时节点zhangsan自动删除</span></span><br><span class="line">[litong, litong0000000003, litong0000000004, zookeeper]</span><br></pre></td></tr></table></figure>

<h3 id="2-2-4-节点监听"><a href="#2-2-4-节点监听" class="headerlink" title="2.2.4 节点监听"></a>2.2.4 节点监听</h3><p>客户端注册监听相关的节点，当节点发生变化（数据变更、节点删除、子目录增删）时，监听机制就是保证Zookeeper能通知各个客户端的有效手段，其主要过程如下：</p>
<ol>
<li><p>主线程中，创建zkClient，zkClient包含两部分：连接器Connector与监听器Listener</p>
</li>
<li><p>客户端把事件注册到监听器中，然后再由Connector线程将当前监听事件发送给Zookeeper服务端。</p>
</li>
<li><p>Zookeeper服务端接受客户端的事件后，将时间添加到服务端事件监听列表中。</p>
</li>
<li><p>当客户端所关心的节点发生变化时，服务器就会将监听结果通过Connector线程又返知给Listener，Listener收到后调用process()方法进行内部处理。</p>
</li>
</ol>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">[zk: localhost:2181(CONNECTED) 2] get /litong  <span class="comment"># 查看当前目录下的值</span></span><br><span class="line">彤哥哥</span><br><span class="line">[zk: localhost:2181(CONNECTED) 3] get -w /litong  <span class="comment"># 客户端1监听/litong节点下值的变化</span></span><br><span class="line">彤哥哥</span><br><span class="line">[zk: localhost:2181(CONNECTED) 2] <span class="built_in">set</span> /litong <span class="string">&quot;彤哥哥，你好啊&quot;</span>  <span class="comment"># 重新打开客户端2修改该节点的值</span></span><br><span class="line">[zk: localhost:2181(CONNECTED) 4]  <span class="comment"># 此时客户端1会同步到客户端2的修改，并且只能监听一次</span></span><br><span class="line">WATCHER::</span><br><span class="line">get</span><br><span class="line">WatchedEvent state:SyncConnected <span class="built_in">type</span>:NodeDataChanged path:/litong</span><br><span class="line"></span><br><span class="line">[zk: localhost:2181(CONNECTED) 4] get /litong</span><br><span class="line">彤哥哥，你好啊</span><br><span class="line">[zk: localhost:2181(CONNECTED) 5] <span class="built_in">ls</span> -w /litong  <span class="comment"># 客户端1监听/litong目录下子节点的变化</span></span><br><span class="line">[]</span><br><span class="line">[zk: localhost:2181(CONNECTED) 3] create /litong/age <span class="comment"># 客户端2进行节点新建</span></span><br><span class="line">Created /litong/age</span><br><span class="line">[zk: localhost:2181(CONNECTED) 6]  <span class="comment"># 那么客户端1会同步客户端2的操作</span></span><br><span class="line">WATCHER::</span><br><span class="line"></span><br><span class="line">WatchedEvent state:SyncConnected <span class="built_in">type</span>:NodeChildrenChanged path:/litong</span><br><span class="line"></span><br><span class="line">[zk: localhost:2181(CONNECTED) 6] <span class="built_in">ls</span> /litong</span><br><span class="line">[age]</span><br></pre></td></tr></table></figure>

<p>注意： <strong>注册一次，只能监听一次，要想多次监听，必须多次注册</strong>。</p>
<h2 id="2-3-Java-API"><a href="#2-3-Java-API" class="headerlink" title="2.3 Java API"></a>2.3 Java API</h2><h3 id="2-3-1-原生API"><a href="#2-3-1-原生API" class="headerlink" title="2.3.1 原生API"></a>2.3.1 原生API</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--  客户端版本与服务端版本最好保持一致--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.zookeeper<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>zookeeper<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.7.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>RELEASE<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">scope</span>&gt;</span>compile<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.demo.zk;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> org.apache.zookeeper.*;</span><br><span class="line"><span class="keyword">import</span> org.apache.zookeeper.data.Stat;</span><br><span class="line"><span class="keyword">import</span> org.junit.Before;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.StandardCharsets;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@className</span>: com.example.demo.zk.ZkClient</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span>: ZK JAVA API使用</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: tong.li</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span>: 2022/5/8 15:02</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@copyright</span>: Tongcheng-Elong Holdings Limited</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ZkClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Zookeeper服务端地址</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">serverHost</span> <span class="operator">=</span> <span class="string">&quot;127.0.0.1:2181&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Zookeeper会话超时时间</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sessionTimeOut</span> <span class="operator">=</span> <span class="number">3000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 全局的zk客户端对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">      <span class="meta">@Before</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 创建Zookeeper客户端</span></span><br><span class="line">        zkClient =  <span class="keyword">new</span> <span class="title class_">ZooKeeper</span>(serverHost, sessionTimeOut, watchedEvent -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;-------------------------------\n全局监听事件处理......&quot;</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;事件类型为：&quot;</span> + watchedEvent.getType());</span><br><span class="line">            System.out.println(<span class="string">&quot;事件发生的路径为：&quot;</span> + watchedEvent.getPath());</span><br><span class="line">            System.out.println(<span class="string">&quot;通知状态为：&quot;</span> + watchedEvent.getState());</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 一次注册监听一次，想要多次，那么再次注册即可</span></span><br><span class="line">                listenerReg();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (KeeperException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">create</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, KeeperException &#123;</span><br><span class="line">        <span class="comment">// 创建节点：路径、节点数据(字节)、访问权限、节点类型</span></span><br><span class="line">        zkClient.create(<span class="string">&quot;/JAVA&quot;</span>, <span class="string">&quot;彤哥哥，欢迎学习JAVA！&quot;</span>.getBytes(StandardCharsets.UTF_8),</span><br><span class="line">                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取节点的值</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getData</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, KeeperException &#123;</span><br><span class="line">        <span class="comment">// 获取节点值：路径、是否监听、节点状态</span></span><br><span class="line">        <span class="type">byte</span>[] value = zkClient.getData(<span class="string">&quot;/JAVA&quot;</span>, <span class="literal">false</span>, <span class="literal">null</span>);</span><br><span class="line">        System.out.println(<span class="keyword">new</span> <span class="title class_">String</span>(value,StandardCharsets.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 获取并监听子节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">getChildren</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, KeeperException &#123;</span><br><span class="line">        listenerReg();</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">1200</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除节点</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> InterruptedException</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> KeeperException</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, KeeperException &#123;</span><br><span class="line">        <span class="type">Stat</span> <span class="variable">stat</span> <span class="operator">=</span> zkClient.exists(<span class="string">&quot;/DATABASE&quot;</span>, <span class="literal">false</span>);</span><br><span class="line">        <span class="keyword">if</span> (stat != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 删除节点：节点路径、版本号</span></span><br><span class="line">            zkClient.delete(<span class="string">&quot;/DATABASE&quot;</span>,stat.getVersion());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断节点是否存在</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> InterruptedException</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> KeeperException</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exists</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, KeeperException &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;节点是否存在：&quot;</span> + ((zkClient.exists(<span class="string">&quot;/DATABASE&quot;</span>, <span class="literal">false</span>) == <span class="literal">null</span>)</span><br><span class="line">                ? <span class="string">&quot;不存在&quot;</span> : <span class="string">&quot;存在&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 监听注册</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> InterruptedException</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> KeeperException</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">listenerReg</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, KeeperException &#123;</span><br><span class="line">        <span class="comment">// 获取并监听子节点节点：路径、是否监听，若不设置Watch，那么走全局的监听事件处理器</span></span><br><span class="line">        List&lt;String&gt; childrens = zkClient.getChildren(<span class="string">&quot;/&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">        <span class="keyword">for</span> (String children : childrens) &#123;</span><br><span class="line">            System.out.println(children);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="2-3-2-Curator框架"><a href="#2-3-2-Curator框架" class="headerlink" title="2.3.2 Curator框架"></a>2.3.2 Curator框架</h3><p>Zookeeper官方原生JAVA API存在如下问题：</p>
<ul>
<li><p>会话连接是异步的，需要自己CountDownLatch处理。</p>
</li>
<li><p>Watch注册一次只能监听一次，需要多次监听，那么就要重复注册</p>
</li>
<li><p>原生API过于抽象，实际编码复杂化。</p>
</li>
<li><p>不支持多节点删除和创建，需要自己去递归。</p>
</li>
<li><p>许多应用场景需要自己实现：分布式锁、注册中心。</p>
</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://curator.apache.org/">Apache Curator</a> 是分布式协调服务<a target="_blank" rel="noopener" href="https://zookeeper.apache.org/">Apache ZooKeeper</a>的 Java/JVM 客户端库。它包括一个高级 API 框架和实用程序，使 Apache ZooKeeper 的使用更加轻松和可靠。它还包括常见用例和扩展的秘诀，例如服务发现和 Java 8 异步 DSL。</p>
<p>Apache Curator 模块介绍：</p>
<ul>
<li><p>curator-recipes：在多数情况下用这个artifact，它包含一些分布式系统常用的功能（recipes），例如分布式锁、领导者选举等</p>
</li>
<li><p>curator-async：包含异步DSL和O/R modeling迁移等功能。</p>
</li>
<li><p>curator-framework：包含Curator框架的高层API，它自动依赖curator-client。</p>
</li>
<li><p>curator-client：相当于官方zkClient中的Zookeeper类。</p>
</li>
<li><p>curator-test：包含一些测试工具。</p>
</li>
<li><p>curator-example：包含几个关于Curator的官方样例。</p>
</li>
<li><p>curator-x-discovery：基于Curator实现的服务发现功能。</p>
</li>
<li><p>curator-x-discovery-server：用于Curator服务发现功能的Restful服务器。</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.example.demo.zk;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.alibaba.fastjson.JSON;</span><br><span class="line"><span class="keyword">import</span> org.apache.curator.RetryPolicy;</span><br><span class="line"><span class="keyword">import</span> org.apache.curator.framework.CuratorFramework;</span><br><span class="line"><span class="keyword">import</span> org.apache.curator.framework.CuratorFrameworkFactory;</span><br><span class="line"><span class="keyword">import</span> org.apache.curator.framework.recipes.cache.CuratorCache;</span><br><span class="line"><span class="keyword">import</span> org.apache.curator.framework.recipes.cache.CuratorCacheListener;</span><br><span class="line"><span class="keyword">import</span> org.apache.curator.retry.ExponentialBackoffRetry;</span><br><span class="line"><span class="keyword">import</span> org.apache.zookeeper.CreateMode;</span><br><span class="line"><span class="keyword">import</span> org.apache.zookeeper.data.Stat;</span><br><span class="line"><span class="keyword">import</span> org.junit.Before;</span><br><span class="line"><span class="keyword">import</span> org.junit.Test;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.StandardCharsets;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@className</span>: com.example.demo.zk.CuratorClient</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span>: ZK Curator JAVA API使用</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span>: tong.li</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@createTime</span>: 2022/5/8 15:02</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@copyright</span>: Tongcheng-Elong Holdings Limited</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CuratorClient</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Zookeeper服务端地址</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">serverHost</span> <span class="operator">=</span> <span class="string">&quot;127.0.0.1:2181&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Zookeeper会话超时时间，毫秒</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">sessionTimeOut</span> <span class="operator">=</span> <span class="number">5000</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Zookeeper连接超时时间，毫秒</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">connectTimeOut</span> <span class="operator">=</span> <span class="number">3000</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 全局的zk客户端对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> CuratorFramework zkClient;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 客户端初始化</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Before</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">init</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 方式一: 静态工厂方法创建Curator客户端，重试策略有多种，这里选择ExponentialBackoffRetry:初始sleep时间和最大重试次数</span></span><br><span class="line">        <span class="type">RetryPolicy</span> <span class="variable">retryPolicy</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ExponentialBackoffRetry</span>(<span class="number">1000</span>, <span class="number">3</span>);</span><br><span class="line">        <span class="comment">// zkClient = CuratorFrameworkFactory.newClient(serverHost, sessionTimeOut, connectTimeOut, retryPolicy);</span></span><br><span class="line">        <span class="comment">// 方式二使用Fluent风格方式创建Curator客户端</span></span><br><span class="line">        zkClient = CuratorFrameworkFactory.builder().connectString(serverHost)</span><br><span class="line">                .sessionTimeoutMs(sessionTimeOut)</span><br><span class="line">                .connectionTimeoutMs(connectTimeOut)</span><br><span class="line">                .retryPolicy(retryPolicy)</span><br><span class="line">                <span class="comment">// 隔离命名空间，当前连接上的所有操作只在指定目录上操作</span></span><br><span class="line">                .namespace(<span class="string">&quot;litong&quot;</span>)</span><br><span class="line">                .build();</span><br><span class="line">        zkClient.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 创建节点</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">create</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 创建1个数据为空的永久节点</span></span><br><span class="line">        zkClient.create().forPath(<span class="string">&quot;/skill&quot;</span>);</span><br><span class="line">        <span class="comment">// 创建1个有数据的永久节点</span></span><br><span class="line">        zkClient.create().forPath(<span class="string">&quot;/books&quot;</span>, <span class="string">&quot;JAVA编程思想&quot;</span>.getBytes(StandardCharsets.UTF_8));</span><br><span class="line">        <span class="comment">// 创建1个临时节点,客户端关闭节点则消失</span></span><br><span class="line">        zkClient.create().withMode(CreateMode.EPHEMERAL).forPath(<span class="string">&quot;/tmp&quot;</span>);</span><br><span class="line">        <span class="comment">// 创建1个永久顺序节点</span></span><br><span class="line">        zkClient.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(<span class="string">&quot;/skill&quot;</span>);</span><br><span class="line">        <span class="comment">// 创建1个临时顺序节点</span></span><br><span class="line">        zkClient.create().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(<span class="string">&quot;/tmp&quot;</span>);</span><br><span class="line">        <span class="comment">// 多级创建，官方的客户端创建前需要子节点判断父节点是否存在，不存在会抛出NoNodeException，而Curator这个方法不需要判断，贼6</span></span><br><span class="line">        zkClient.create().creatingParentContainersIfNeeded().forPath(<span class="string">&quot;/a/b/c&quot;</span>, <span class="string">&quot;我的路径是a/b/a&quot;</span>.getBytes(StandardCharsets.UTF_8));</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">120</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     *  获取节点</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">get</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 获取节点值,并获取Stat</span></span><br><span class="line">        <span class="type">Stat</span> <span class="variable">stat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Stat</span>();</span><br><span class="line">        <span class="type">byte</span>[] value = zkClient.getData().storingStatIn(stat).forPath(<span class="string">&quot;/books&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;节点值: &quot;</span> + <span class="keyword">new</span> <span class="title class_">String</span>(value,StandardCharsets.UTF_8));</span><br><span class="line">        System.out.println(<span class="string">&quot;节点状态: &quot;</span> + JSON.toJSONString(stat));</span><br><span class="line">        <span class="comment">// 获取指定节点下的所有子节点</span></span><br><span class="line">        System.out.println(<span class="string">&quot;/litong/a下所有的子节点路径:&quot;</span> + zkClient.getChildren().forPath(<span class="string">&quot;/a&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 更新数据</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">updateData</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        zkClient.setData().forPath(<span class="string">&quot;/books&quot;</span>, <span class="string">&quot;JAVA从入门到劝退&quot;</span>.getBytes(StandardCharsets.UTF_8));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 删除节点</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">delete</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 删除节点，只能删除当前子节点</span></span><br><span class="line">        zkClient.delete().forPath(<span class="string">&quot;/skill0000000004&quot;</span>);</span><br><span class="line">        <span class="comment">// 递归删除节点：/a/b/c</span></span><br><span class="line">        zkClient.delete().deletingChildrenIfNeeded().forPath(<span class="string">&quot;/a&quot;</span>);</span><br><span class="line">        <span class="comment">// 指定版本删除</span></span><br><span class="line">        <span class="comment">// zkClient.delete().withVersion(10).forPath(&quot;/skill0000000004&quot;);</span></span><br><span class="line">        <span class="comment">// guaranteed()接口是一个保障措施，只要客户端会话有效，那么Curator会在后台持续进行删除操作，直到删除节点成功。</span></span><br><span class="line">        <span class="comment">// zkClient.delete().guaranteed().withVersion(10).forPath(&quot;/skill0000000004&quot;);</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 判断节点是否存在</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">exists</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;节点是否存在：&quot;</span> + (zkClient.checkExists().forPath(<span class="string">&quot;/books&quot;</span>) == <span class="literal">null</span> ? <span class="string">&quot;不存在&quot;</span> : <span class="string">&quot;存在&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 监听注册</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Test</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">listenerReg</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// NodeCache类已被Curator最新版本启用，建议使用CuratorCache</span></span><br><span class="line"><span class="comment">//        NodeCache nodeCache = new NodeCache(zkClient, &quot;/books&quot;);</span></span><br><span class="line"><span class="comment">//        nodeCache.getListenable().addListener(() -&gt;</span></span><br><span class="line"><span class="comment">//                System.out.println( nodeCache.getPath() + &quot;节点有变更，变更后的值:&quot; +  new String(nodeCache.getCurrentData().getData(),StandardCharsets.UTF_8)));</span></span><br><span class="line"><span class="comment">//        // 开启监听，如果传true，那么延迟监听</span></span><br><span class="line"><span class="comment">//        nodeCache.start(true);</span></span><br><span class="line"></span><br><span class="line">        <span class="type">CuratorCache</span> <span class="variable">cache</span> <span class="operator">=</span> CuratorCache.build(zkClient, <span class="string">&quot;/books&quot;</span>);</span><br><span class="line">        <span class="comment">// 添加节点更新与删除的监听事件</span></span><br><span class="line">        <span class="type">CuratorCacheListener</span> <span class="variable">listener</span> <span class="operator">=</span> CuratorCacheListener.builder().forChanges((oldNode, node) -&gt; System.out.println(oldNode.getPath() + <span class="string">&quot;节点有变更，&quot;</span> + <span class="string">&quot;变更前的值:&quot;</span> +  <span class="keyword">new</span> <span class="title class_">String</span>(oldNode.getData(),StandardCharsets.UTF_8) + <span class="string">&quot;, &quot;</span></span><br><span class="line">                + <span class="string">&quot;变更后的值:&quot;</span> +  <span class="keyword">new</span> <span class="title class_">String</span>(node.getData(),StandardCharsets.UTF_8)))</span><br><span class="line">                .forDeletes(oldNode -&gt; System.out.println(String.format(<span class="string">&quot;节点%s被删除了...&quot;</span>,oldNode.getPath())))</span><br><span class="line">                .build();</span><br><span class="line">        cache.listenable().addListener(listener);</span><br><span class="line">        cache.start();</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">120</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="3-深究流程"><a href="#3-深究流程" class="headerlink" title="3. 深究流程"></a>3. 深究流程</h1><h2 id="3-1-数据写入机制"><a href="#3-1-数据写入机制" class="headerlink" title="3.1 数据写入机制"></a>3.1 数据写入机制</h2><h3 id="3-1-1-Leader写入"><a href="#3-1-1-Leader写入" class="headerlink" title="3.1.1 Leader写入"></a>3.1.1 Leader写入</h3><p><img src="https://ltyeamin.github.io/imgs/2022/05/20220522154941.png"></p>
<h3 id="3-1-2-Follower写入"><a href="#3-1-2-Follower写入" class="headerlink" title="3.1.2 Follower写入"></a>3.1.2 Follower写入</h3><p><img src="https://ltyeamin.github.io/imgs/2022/05/20220522155443.png"></p>
<h2 id="3-2-选举机制"><a href="#3-2-选举机制" class="headerlink" title="3.2 选举机制"></a>3.2 选举机制</h2><p>Zookeeper默认的算法是org.apache.zookeeper.server.quorum.FastLeaderElection，采用投票数大于半数则胜出的逻辑。</p>
<h3 id="3-2-1-相关概念"><a href="#3-2-1-相关概念" class="headerlink" title="3.2.1 相关概念"></a>3.2.1 相关概念</h3><ul>
<li><p>Zookeeper服务器节点ID：假设3台机器节点编号为1、2、3，则有编号越大，选举权重越大。</p>
</li>
<li><p>选举状态：Looking（竞选状态）、Following（跟随状态，同步Leader状态，参与选举投票）、Observing（观察状态，同步Leader状态，不参与选举投票）、Leading（领导状态）。</p>
</li>
<li><p>数据版本号：服务器中存放节点的最新数据，随着值越大，数据也就更新，选举权重就更大。</p>
</li>
<li><p>逻辑时钟：投票次数，同一轮投票过程中的逻辑时钟值是一致的。每投完一轮票，这个逻辑时钟值会增加。然后与接收到的其它服务器返回的投票信息中的数值相比，根据不同的值做出不同的判断。</p>
</li>
</ul>
<h3 id="3-2-2-全新集群选举"><a href="#3-2-2-全新集群选举" class="headerlink" title="3.2.2 全新集群选举"></a>3.2.2 全新集群选举</h3><p>假设目前有5台服务器，每台服务器均没有数据，它们的编号分别是1,2,3,4,5，按编号依次启动，它们的选择举过程如下：</p>
<ul>
<li><p>服务器1启动，给自己投票，然后发投票信息，由于其它机器还没有启动所以它收不到反馈信息，服务器1的状态一直属于 Looking。</p>
</li>
<li><p>服务器2启动，给自己投票，同时与之前启动的服务器 1 交换结果，由于服务器 2 的编号大所以服务器2胜出，但此时投票数没有大于半数，所以两个服务器的状态依然是LOOKING。</p>
</li>
<li><p>服务器3启动，给自己投票，同时与之前启动的服务器1,2交换信息，由于服务器3的编号最大所以服务器3胜出，此时投票数正好大于半数，所以服务器3成为领导者，服务器 1,2 为Following。</p>
</li>
<li><p>服务器4启动，给自己投票，同时与之前启动的服务器 1,2,3 交换信息，尽管服务器 4 的编号大，但之前服务器3已经胜出，所以服务器4只能成为小弟。</p>
</li>
<li><p>服务器5启动，后面的逻辑同服务器4成为小弟。</p>
</li>
</ul>
<h3 id="3-2-3-非全新集群选举"><a href="#3-2-3-非全新集群选举" class="headerlink" title="3.2.3 非全新集群选举"></a>3.2.3 非全新集群选举</h3><p>对于运行正常的 zookeeper集群，中途有机器down掉， 需要重新选举时，选举过程就需要加入数据ID、 服务器ID和逻辑时钟。</p>
<ul>
<li><p>数据ID(ZXID)：数据新的version就大，数据每次更新都会更新version。 </p>
</li>
<li><p>服务器ID(SID)：配置里myid的值，每个机器一个。 </p>
</li>
<li><p>逻辑时钟（Epoch）：这个值从0开始递增,每次选举对应一个值。 如果在同一次选举中,这个值是一致的。</p>
</li>
</ul>
<p>这样选举的标准就变成：</p>
<ol>
<li><p>逻辑时钟小的选举结果被忽略，重新投票。</p>
</li>
<li><p>统一逻辑时钟后，数据id大的胜出。</p>
</li>
<li><p>数据 id 相同的情况下， 服务器 id 大的胜出。</p>
</li>
</ol>
</section>
    <!-- Tags START -->
    
      <div class="tags">
        <span>Tags:</span>
        
  <a href="/tags#Zookeeper" >
    <span class="tag-code">Zookeeper</span>
  </a>

      </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/2022/05/02/%E3%80%90%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95%E3%80%91%E9%AB%98%E6%95%88%E7%9A%84%E4%BD%8D%E8%BF%90%E7%AE%97/">
        <span class="nav-arrow">← </span>
        
          【数据结构与算法】高效的位运算
        
      </a>
    
    
      <a class="nav-right" href="/2023/01/07/%E5%85%A8%E6%96%B9%E4%BD%8D%E7%8E%A9%E8%BD%ACRedis/">
        
          全方位玩转Redis
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
      <div class="money-like">
        <div class="reward-btn">
          赏
          <span class="money-code">
            <span class="alipay-code">
              <div class="code-image"></div>
              <b>使用支付宝打赏</b>
            </span>
            <span class="wechat-code">
              <div class="code-image"></div>
              <b>使用微信打赏</b>
            </span>
          </span>
        </div>
        <p class="notice">若你觉得我的文章对你有帮助，欢迎点击上方按钮对我打赏</p>
      </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
      <div class="qrcode">
        <canvas id="share-qrcode"></canvas>
        <p class="notice">扫描二维码，分享此文章</p>
      </div>
    
    <!-- 二维码 END -->
    
      <!-- Utterances START -->
      <div id="utterances"></div>
      <script src="https://utteranc.es/client.js"
        repo="ltyeamin/blogtalks"
        issue-term="pathname"
        theme="github-light"
        crossorigin="anonymous"
        async></script>    
      <!-- Utterances END -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
    <aside class="catalog-container">
  <div class="toc-main">
    <strong class="toc-title">Catalog</strong>
    
      <ol class="toc-nav"><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#1-%E5%89%8D%E8%A8%80"><span class="toc-nav-text">1.前言</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#1-1-%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF"><span class="toc-nav-text">1.1 应用场景</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#1-2-%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6"><span class="toc-nav-text">1.2 工作机制</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#1-3-%E9%9B%86%E7%BE%A4%E8%A7%92%E8%89%B2"><span class="toc-nav-text">1.3 集群角色</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#1-4-%E5%8A%9F%E8%83%BD%E7%89%B9%E6%80%A7"><span class="toc-nav-text">1.4 功能特性</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#1-5-%E6%95%B0%E6%8D%AE%E6%A8%A1%E5%9E%8B"><span class="toc-nav-text">1.5 数据模型</span></a></li></ol></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#2-Zookeeper%E4%BD%BF%E7%94%A8"><span class="toc-nav-text">2. Zookeeper使用</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#2-1-Zookeeper%E6%90%AD%E5%BB%BA"><span class="toc-nav-text">2.1 Zookeeper搭建</span></a></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#2-2-Zookeeper%E4%BD%BF%E7%94%A8"><span class="toc-nav-text">2.2 Zookeeper使用</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#2-2-1-%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4%E8%A1%8C"><span class="toc-nav-text">2.2.1 基本命令行</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#2-2-2-%E8%8A%82%E7%82%B9%E7%B1%BB%E5%9E%8B"><span class="toc-nav-text">2.2.2 节点类型</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#2-2-3-%E8%8A%82%E7%82%B9%E6%93%8D%E4%BD%9C"><span class="toc-nav-text">2.2.3 节点操作</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#2-2-4-%E8%8A%82%E7%82%B9%E7%9B%91%E5%90%AC"><span class="toc-nav-text">2.2.4 节点监听</span></a></li></ol></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#2-3-Java-API"><span class="toc-nav-text">2.3 Java API</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#2-3-1-%E5%8E%9F%E7%94%9FAPI"><span class="toc-nav-text">2.3.1 原生API</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#2-3-2-Curator%E6%A1%86%E6%9E%B6"><span class="toc-nav-text">2.3.2 Curator框架</span></a></li></ol></li></ol></li><li class="toc-nav-item toc-nav-level-1"><a class="toc-nav-link" href="#3-%E6%B7%B1%E7%A9%B6%E6%B5%81%E7%A8%8B"><span class="toc-nav-text">3. 深究流程</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#3-1-%E6%95%B0%E6%8D%AE%E5%86%99%E5%85%A5%E6%9C%BA%E5%88%B6"><span class="toc-nav-text">3.1 数据写入机制</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#3-1-1-Leader%E5%86%99%E5%85%A5"><span class="toc-nav-text">3.1.1 Leader写入</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#3-1-2-Follower%E5%86%99%E5%85%A5"><span class="toc-nav-text">3.1.2 Follower写入</span></a></li></ol></li><li class="toc-nav-item toc-nav-level-2"><a class="toc-nav-link" href="#3-2-%E9%80%89%E4%B8%BE%E6%9C%BA%E5%88%B6"><span class="toc-nav-text">3.2 选举机制</span></a><ol class="toc-nav-child"><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#3-2-1-%E7%9B%B8%E5%85%B3%E6%A6%82%E5%BF%B5"><span class="toc-nav-text">3.2.1 相关概念</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#3-2-2-%E5%85%A8%E6%96%B0%E9%9B%86%E7%BE%A4%E9%80%89%E4%B8%BE"><span class="toc-nav-text">3.2.2 全新集群选举</span></a></li><li class="toc-nav-item toc-nav-level-3"><a class="toc-nav-link" href="#3-2-3-%E9%9D%9E%E5%85%A8%E6%96%B0%E9%9B%86%E7%BE%A4%E9%80%89%E4%B8%BE"><span class="toc-nav-text">3.2.3 非全新集群选举</span></a></li></ol></li></ol></li></ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'http://example.com/2022/05/07/Zookeeper专题-Zookeeper入门/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function() {
      $(this).attr('src', '/css/images/error_icon.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function() {
      var src = $(this).attr('src')
      if (src !== '/css/images/error_icon.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="'+ src +'" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function() {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


  <script>
    var qr = new QRious({
      element: document.getElementById('share-qrcode'),
      value: document.location.href
    });
  </script>






    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
  <p class="copyright">
    &copy; 2024 | Proudly powered by <a href="https://hexo.io" target="_blank">Hexo</a>
    <br>
    Theme by <a target="_blank" rel="noopener" href="https://github.com/ltyeamin">tong.li</a>
  </p>
</footer>

<script>
  function async(u, c) {
    var d = document, t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async("https://cdn.staticfile.org/fastclick/1.0.6/fastclick.min.js", function(){
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async("https://cdn.staticfile.org/highlight.js/9.12.0/highlight.min.js", function(){
    $('figure pre').each(function(i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      hljs.configure({useBR: true});
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>
<!-- Baidu Tongji -->



<script src='https://cdn.staticfile.org/mermaid/8.11.2/mermaid.min.js'></script>



<script src="/js/script.js"></script>


  </body>
</html>